package com.api.oa.services.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.api.oa.common.UserType;
import com.api.oa.config.UserContext;
import com.api.oa.mapper.YQFaceMapper;
import com.api.oa.mapper.YQPatientMapper;
import com.api.oa.mapper.YQUserMapper;
import com.api.oa.model.entity.YQFace;
import com.api.oa.model.entity.YQPatient;
import com.api.oa.model.entity.YQUser;
import com.api.oa.model.params.YQFaceParam;
import com.api.oa.model.vo.OaOrgTreeVO;
import com.api.oa.model.vo.TencentFaceVO;
import com.api.oa.model.vo.YQFaceVO;
import com.api.oa.services.YQFaceService;
import com.api.oa.utils.LocalRedis;
import com.github.jaiimageio.impl.common.ImageUtil;
import net.coobird.thumbnailator.Thumbnails;
import org.bouncycastle.util.encoders.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.transaction.annotation.Transactional;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.*;


@Service
public class YQFaceServiceImpl implements YQFaceService {

    @Autowired
    private YQFaceMapper mapper;
    @Autowired
    YQUserMapper yqUserMapper;


    @Autowired
    YQPatientMapper yqPatientMapper;

    @Autowired
    LocalRedis localRedis;

    @Override
    public Integer add(YQFace entity) throws Exception {
        YQFace oneByUserId = mapper.findOneByUserId(entity.getUserId());
        if (oneByUserId != null) {
            throw new Exception("已经注册过了");
        }
        return mapper.insert(entity);
    }

    @Override
    public void remove(Long id) {
        mapper.delete(id);
    }

    @Override
    public Integer update(YQFace entity) {
        return mapper.update(entity);
    }

    @Override
    public PageInfo<YQFaceVO> list(String insCode, String keyWord, String role, Integer pageIndex, Integer pageSize) {
        if (UserContext.getRole() == UserType.INS) {
            insCode = UserContext.getInsCode();
        }
        PageHelper.startPage(pageIndex, pageSize);
        PageHelper.orderBy("yq_user_face.createdAt desc, yq_user_face.faceId desc");
        return new PageInfo<>(mapper.list(insCode, keyWord, role));
    }

    @Override
    public YQFace one(Long id) {
        return mapper.findOne(id);
    }


    @Override
    public YQFace verify(Long id) throws Exception {
        YQFace oneByUserId = mapper.findOneByUserId(id);
        if (oneByUserId == null) throw new Exception("未发现人脸注册，请注册");
        return oneByUserId;
    }

    @Override
    @Transactional
    public int reg(YQFaceParam entity) throws Exception {
        YQFace oneByUserId = mapper.findOneByUserId(entity.getUserId());
        if (oneByUserId != null) {
            throw new Exception("已经注册过了");
        }
        if (UserContext.getRole() == UserType.PATIENT) { //如果是患者
            if (entity.getLng() != null && entity.getLng() > 0) { //患者的脸 并进行更新坐标
                YQPatient one = yqPatientMapper.findOne(entity.getUserId());
                yqPatientMapper.updateLatLngByUserId(one.getPatientIdCard(), entity.getLat(), entity.getLng());
            }
        }
        YQFace yqFace = new YQFace();
        yqFace.setFaceBlob(entity.getFaceBlob());
        yqFace.setFeatureBlob(entity.getFeatureBlob());
        yqFace.setCreatedAt(DateUtil.date());
        yqFace.setUserId(entity.getUserId());
        return mapper.insert(yqFace);
    }

    @Override
    public PageInfo<YQFaceVO> patientFace(Long userId, String attendType, String keyWord, Integer pageIndex, Integer pageSize) throws Exception {
        YQUser one = yqUserMapper.findOne(userId);
        if (one.getBelongInsCode() == null) {
            throw new Exception("您未签约机构");
        }
        PageHelper.startPage(pageIndex, pageSize);
        PageHelper.orderBy("yq_user_face.createdAt desc,v_yq_patient_user.userId desc");
        return new PageInfo<>(mapper.patientFace(one.getBelongInsCode(), attendType, keyWord));
    }


    @Value("${face.webankAppId}")
    String webankAppId;

    @Value("${face.secret}")
    private String secret;

    @Value("${face.license}")
    private String keyLicence;

    @Override
    public TencentFaceVO verifyTencent(Long id) throws Exception {
        TencentFaceVO tencentFaceVO = new TencentFaceVO();
        YQUser one = yqUserMapper.findOne(id);
        if (one.getBelongInsCode() == null) {
            throw new Exception("您未签约机构");
        }

        YQFace oneByUserId = mapper.findOneByUserId(id);
        if (oneByUserId == null) throw new Exception("未录入人脸");
        byte[] face = oneByUserId.getFaceBlob();
        if (oneByUserId.getFaceBlob().length > 500 * 1024) {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(oneByUserId.getFaceBlob());
            BufferedImage read = ImageIO.read(byteArrayInputStream);
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            BufferedImage bufferedImage = Thumbnails.of(read).size(read.getWidth() / 3, read.getHeight() / 3).asBufferedImage();
            ImageIO.write(bufferedImage, "jpg", byteArrayOutputStream);
            face = byteArrayOutputStream.toByteArray();
        }
        Map<String, Object> params = new HashMap<>();
        String nonce = RandomUtil.randomString(32);
        String orderNo = RandomUtil.randomString(32);
        String version = "1.0.0";
        Object ticketObj = localRedis.get("face.api_ticket");
        if (ticketObj == null) {
            String ticket = getTicket();
            localRedis.set("face.api_ticket", ticket, 3600);
            ticketObj = ticket;
        }
        List<String> values = new ArrayList<>();
        values.add(version);
        values.add(nonce);
        values.add(webankAppId);
        values.add(String.valueOf(id));
        String sha1 = sign(values, (String) ticketObj);
        params.put("version", version);
        params.put("sign", sha1);
        params.put("userId", id);
        params.put("nonce", nonce);
        params.put("orderNo", orderNo);
        params.put("sourcePhotoStr", Base64.toBase64String(face));
        params.put("sourcePhotoType", 1);

        String post = HttpRequest.post("https://miniprogram-kyc.tencentcloudapi.com/api/server/getfaceid?orderNo=" + orderNo).body(JSONUtil.toJsonStr(params), "application/json").execute().body();
        JSONObject jsonObject = JSONUtil.parseObj(post);
        if (jsonObject.getInt("code") != 0) {
            throw new Exception("人脸服务异常:" + jsonObject.getStr("msg"));
        }
        JSONObject result = jsonObject.getJSONObject("result");
        List<String> nonceValue = new ArrayList<>();
        nonceValue.add(version);
        nonceValue.add(nonce);
        nonceValue.add(webankAppId);
        nonceValue.add(String.valueOf(id));
        String nonceSign = sign(nonceValue, getNONCETicket(String.valueOf(id)));
        tencentFaceVO.setFaceId(result.getStr("faceId"));
        tencentFaceVO.setNonce(nonce);
        tencentFaceVO.setOrderNo(orderNo);
        tencentFaceVO.setSign(nonceSign);
        tencentFaceVO.setVersion(version);
        tencentFaceVO.setUserId(String.valueOf(id));
        tencentFaceVO.setKeyLicence(keyLicence);
        tencentFaceVO.setAppId(webankAppId);
        return tencentFaceVO;
    }


    public static String sign(List<String> values, String ticket) {
        if (values == null) {
            throw new NullPointerException("values is null");
        }
        values.removeAll(Collections.singleton(null));// remove null
        values.add(ticket);
        java.util.Collections.sort(values);
        StringBuilder sb = new StringBuilder();
        for (String s : values) {
            sb.append(s);
        }
        return SecureUtil.sha1(sb.toString());
    }


    private String getTicket() {
        Object accessToken = localRedis.get("face.accessToken");
        if (accessToken == null) {
            String s = accessToken();
            localRedis.set("face.accessToken", s, 7200);
            accessToken = s;
        }
        String url = String.format("https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket?app_id=%s&access_token=%s&type=SIGN&version=1.0.0", webankAppId, String.valueOf(accessToken));
        String s = HttpUtil.get(url);
        JSONObject jsonObject = JSONUtil.parseObj(s);
        if (jsonObject.getInt("code") != 0) return null;
        JSONArray tickets = jsonObject.getJSONArray("tickets");
        JSONObject o = (JSONObject) tickets.get(0);
        return o.getStr("value");
    }

    private String getNONCETicket(String id) {
        Object accessToken = localRedis.get("face.accessToken");
        if (accessToken == null) {
            String s = accessToken();
            localRedis.set("face.accessToken", s, 7200);
            accessToken = s;
        }
        String url = String.format("https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket?app_id=%s&access_token=%s&type=NONCE&version=1.0.0&user_id=%s", webankAppId, String.valueOf(accessToken), id);
        String s = HttpUtil.get(url);
        JSONObject jsonObject = JSONUtil.parseObj(s);
        if (jsonObject.getInt("code") != 0) return null;
        JSONArray tickets = jsonObject.getJSONArray("tickets");
        JSONObject o = (JSONObject) tickets.get(0);
        return o.getStr("value");
    }


    private String accessToken() {
        String url = String.format("https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token?app_id=%s&secret=%s&grant_type=client_credential&version=1.0.0", webankAppId, secret);
        String s = HttpUtil.get(url);
        JSONObject jsonObject = JSONUtil.parseObj(s);
        return jsonObject.getStr("access_token");
    }


}
